#include "schedule.h"
#include "config.h"
#include "pcb.h"
#include "print.h"
#include <stdio.h>
#include <stdlib.h>

static int cmp_pri(const void *a, const void *b) {
  return (*((struct PCB **)b))->priority - (*((struct PCB **)a))->priority;
}

static void sort_process_arr(struct PCB *arr[], size_t n) {
  qsort(arr, n, sizeof(struct PCB *), cmp_pri);
}

static void init_priority_schedule(struct PCB **head, const int proc_count,
                                   struct PCB **tail) {
  struct PCB *process_arr[proc_count], *tmp_tail, *processes = *head;
  for (int i = 0; i < proc_count; ++i, processes = processes->next) {
    process_arr[i] = processes;
  }

  sort_process_arr(process_arr, proc_count);

  *head = tmp_tail = process_arr[0];
  for (int i = 1; i < proc_count; ++i, tmp_tail = tmp_tail->next) {
    tmp_tail->next = process_arr[i];
  }

  tmp_tail->next = NULL;
  *tail = tmp_tail;
}

static void insert_process(struct PCB **head, struct PCB **tail,
                           struct PCB *run) {
  if (run->need_time == 0) {
    run->state = FINISH;
    (*tail)->next = run;
  } else {
    struct PCB *p, *q;
    for (q = *head; q && q->priority >= run->priority && q->need_time;
         q = q->next) {
      p = q;
    }
    if (q == *head) {
      run->next = q;
      *head = run;
    } else {
      run->next = p->next;
      p->next = run;
    }
  }

  if ((*tail)->next == run) {
    *tail = run;
    run->next = NULL;
  }
}

void priority_schedule(struct PCB **processes) {
  printf("PRIORITY SCHEDULE\n");
  struct PCB *head = *processes, *tail, *run;
  int cpu_time = 0;

  init_priority_schedule(&head, proc_count, &tail);
  init_print(head);

  print_init();

  for (; head->need_time > 0; ++cpu_time) {
    run = head;
    run->state = RUNNING;
    head = head->next;

    print_helper(cpu_time, run, head);

    run->need_time--;
    run->priority -= 3;
    run->cpu_time++;
    run->state = WAITING;

    insert_process(&head, &tail, run);
    while ((getchar() != '\n'))
      ;
  }
  print_helper(cpu_time, NULL, head);

  free_print();

  *processes = head;
}

// -------------------------------------------

void round_robin_schedule(struct PCB **processes) {
  printf("ROUND ROBIN SCHEDULE\n");
  struct PCB *head = *processes, *tail = NULL, *run;
  for (tail = head; tail->next; tail = tail->next)
    ;
  int cpu_time = 0;

  init_print(head);

  print_init();

  while (head->need_time > 0) {
    run = head;
    run->state = RUNNING;
    head = head->next;
    for (int i = run->time_slice; i > 0; --i) {
      print_helper(cpu_time, run, head);

      run->priority -= 3;
      run->cpu_time++;
      ++cpu_time;
      if (--run->need_time == 0) {
        break;
      }
    }

    // next_slice:
    if (tail != run) {
      struct PCB *p = run;
      // run = run->next;
      p->next = tail->next;
      tail->next = p;

      if (p->need_time == 0) {
        p->state = FINISH;
      } else {
        p->state = WAITING;
        tail = tail->next;
      }
    } else {
      run->next = head;
      head = run;
      if (run->need_time == 0) {
        run->state = FINISH;
      }
    }

    while ((getchar() != '\n'))
      ;
  }
  print_helper(cpu_time, NULL, head);

  free_print();

  *processes = head;
}